<?php

namespace Platform\Models;

use Phalcon\Mvc\Model;

/**
 * 基础服务类
 */
class BaseModel extends \Phalcon\Mvc\Model implements \Phalcon\Mvc\ModelInterface {

    /**
     * 数据库连接对象
     * @var \Phalcon\Db\Adapter\Pdo\Mysql
     */
    public $db;
    public $redisCache;
    public $mongoCache;
    public $pk = "id";

    public function getPk() {
        return $this->pk;
    }

    public function setPk($pk) {
        $this->pk = $pk;
    }

    const redisCounterSyncNumber = 1;

    public function initialize() {
//        $this->redisCache = $this->getDI()->getShared("redisCache");
//        $this->mongoCache = $this->getDI()->getShared("mongoCache");

//        /** 不对空字段进行validation校验 */
//        self::setup(array(
//            'notNullValidations' => false
//        ));
    }

    /**
     * 暂未测试
     * 封装phalcon model的update方法，实现仅更新数据变更字段，而非所有字段更新
     * @param array|null $data
     * @param null $whiteList
     * @return bool
     */
    public function iupdate(array $data = null, $whiteList = null) {
        if (count($data) > 0) {
            $attributes = $this->getModelsMetaData()->getAttributes($this);
            $this->skipAttributesOnUpdate(array_diff($attributes, array_keys($data)));
        }
        return parent::update($data, $whiteList);
    }

    /**
     * 实例数组
     * @var array
     */
    private static $instance = [];
    static private $_instance = null; //当前model服务实例

    public static function getInstance($objName = "") {
        if (!$objName) {
            $objName = get_called_class();
        }
        if (isset(self::$instance[$objName])) {
            self::$_instance = self::$instance[$objName];
        } else {
            self::$_instance = new $objName();
            self::$instance[$objName] = self::$_instance;
        }
        return self::$_instance;
    }

    /**
     * 根据ID进行逻辑删除
     * @param type $id
     * @param string $fieldName
     */
    public function logicDeleteDataById($id, $fieldName = FALSE, $value = false) {
        if (!$fieldName) {
            $fieldName = "isDel";
        }
        if ($value !== FALSE) {
            $is_del = $value;
        } else {
            $is_del = IS_DEL_YES;
        }
        $phql = "UPDATE " . get_called_class() . " SET $fieldName = " . $is_del . " WHERE $this->pk = :id:";
        $result = $this->getModelsManager()->executeQuery($phql, array(
            'id' => $id
        ));
        return $result;
    }

    /**
     * 根据条件查询一条数据
     * @param $id
     * @param $columns
     */
    public function getDataById($id, $columns = '', $returnObject = false) {
        $query = [
            "conditions" => "$this->pk = :id:",
            "bind" => [
                "id" => $id
            ],
        ];
        if ($columns) {
            $query['columns'] = $columns;
        }
        $data = self::findFirst($query);
        if ($data && $returnObject == false) {
            $data = $data->toArray();
        }
        return $data;
    }

    /**
     * 根据ID数组查询数据
     * @param Array $idArr
     * @return modelList 
     */
    public function getDataListByIdstr($idArr, $columns) {
        $query = [
            "conditions" => "$this->pk  in ({id:array})",
            "bind" => [
                "id" => $idArr
            ],
        ];
        if ($columns) {
            $query['columns'] = $columns;
        }
        $dataList = self::find($query);
        if ($dataList) {
            $dataList = $dataList->toArray();
        }
        return $dataList;
    }

    /**
     * 根据数组里面的id获取原数据,需要保留原数组的id,循环id数组按原对应的数组排序把数组返回回去
     * @param type $list   原数组
     * @param type $list_column_name  原数组里面的key名
     * @param type $columns  要查询的的数据库字段
     * @param type $columns  原ID返回在返回出去的数组里面的新key名
     * @return type
     */
    public function getDateListBySelectDate($list, $list_column_name, $columns, $idNick) {
        foreach ($list as $key => $value) {
            $oldIdArr[] = $value['id'];
            $idArr[] = $value[$list_column_name];
        }
        $list = $this->getDataListByIdstr($idArr, $columns);
        if (!$list) {
            return array();
        }
        //这里先循环把主键做key更改数组
        foreach ($list as $key => $value) {
            $keyList[$value[$this->pk]] = $value;
        }
        //循环id数组按原对应的数组排序把数组返回回去，并且保留原数组的id作为下次分页查询的条件,以及查询出结果的对象id
        foreach ($idArr as $key => $value) {
            if ($keyList[$value]) {
                $keyList[$value][$idNick] = strval($value); //保持查询出结果的对象的id
                $keyList[$value]['id'] = $oldIdArr[$key]; //保持原id
                $result[] = $keyList[$value];
            }
        }
        return $result;
    }

    /**
     * redis自增,并且同步入数据库
     * @param type $redisKey
     * @param type $id
     * @param type $syncColumn
     * @return type 
     */
    public function redisIncrement($redisKey, $id, $syncColumn) {
        $number = $this->redisCache->get($redisKey);
        $number = intval($number) + 1;
        if ($number % self::redisCounterSyncNumber == 0) {
            $phql = "UPDATE " . get_called_class() . " SET $syncColumn = " . ($number) . " WHERE $this->pk = :id:";
            $this->getModelsManager()->executeQuery($phql, array(
                'id' => $id
            ));
        }
        $this->redisCache->incr($redisKey);
        return $number;
    }

    /**
     * redis自减,并且同步入数据库
     * @param type $redisKey
     * @param type $id
     * @param type $syncColumn
     * @return type
     */
    public function redisDecrement($redisKey, $id, $syncColumn) {
        $number = $this->redisCache->get($redisKey);
        $number = intval($number) - 1;
        if ($number % self::redisCounterSyncNumber == 0) {
            $phql = "UPDATE " . get_called_class() . " SET $syncColumn = " . ($number) . " WHERE $this->pk = :id:";
            $this->getModelsManager()->executeQuery($phql, array(
                'id' => $id
            ));
        }
        $this->redisCache->decr($redisKey);
        return $number;
    }

    /**
     * 根据条件与页码查询总数据以及数据列表分页信息
     * @param array $condition  查询条件
     * @param String $orderStr 排序条件
     * @param array $fieldArr  要查询的字段
     * @param int $page  分页
     * @param int $pagesize 分页条件
     * @param String $groupStr 分组条件
     * @return modelList
     */
    public function getCountNumber($condition, $groupStr = "") {
        $conditionStr = "";
        $bind = array();

        foreach ($condition as $key => $value) {

            if ($conditionStr) {
                $conditionStr .= " AND ";
            }
            $conditionStr .= $this->createConditionStr($key, $value, $bind);

        }

        $query = [
            "conditions" => $conditionStr,
            "bind" => $bind,
        ];
        if ($groupStr) {
            $query['group'] = $groupStr;
        }
        $count = self::count($query);

        return $count;
    }

    /**
     * 根据条件与页码查询总数据以及数据列表分页信息
     * @param array $condition  查询条件
     * @param array $fieldArr  要查询的字段
     * @param String $orderStr 排序条件
     * @param int $page  分页
     * @param int $pagesize 分页条件
     * @param String $groupStr 分组条件
     * @return modelList
     */
    public function getLists($condition, $fieldArr = array(), $orderStr = "", $page = 1, $pagesize = 10, $groupStr = "") {

        $conditionStr = "";
        $bind = array();
        foreach ($condition as $key => $value) {
            if ($conditionStr) {
                $conditionStr .= " AND ";
            }
            $conditionStr .= $this->createConditionStr($key, $value, $bind);
        }


        $query = [
            "conditions" => $conditionStr,
            "bind" => $bind,
        ];

        if ($groupStr) {
            $query['group'] = $groupStr;
        }
        if ($fieldArr && is_array($fieldArr)) {
            $query['columns'] = implode(",", $fieldArr);
        }
        if ($orderStr) {
            $query['order'] = $orderStr;
        }
        if ($page != 0) {
            $query['offset'] = $pagesize * ($page - 1);
        }
        if ($pagesize != 0) {
            $query['limit'] = $pagesize;
        }

        $list = self::find($query);

        if ($list) {
            $list = $list->toArray();
        } else {
            $list = array();
        }

        return $list;
    }

//    表达式不分大小写，支持的查询表达式有下面几种，分别表示的含义是：
//    表达式	含义	协助记忆
//    EQ	等于（=）	equal
//    NEQ	不等于（<>）	not equal
//    GT	大于（>）	greater
//    EGT	大于等于（>=）	equal or greater
//    LT	小于（<）	less than
//    ELT	小于等于（<=）	equal or less than
//    LIKE	模糊查询	
//    [NOT] BETWEEN	（不在）区间查询	
//    [NOT] IN	（不在）IN 查询	
//    EXP	表达式查询，支持SQL语法	expression
    public function createConditionStr($key, $value, &$bind) {
        if (is_array($value)) {
            $expression = strtoupper($value[0]);
            switch ($expression) {
                case "EQ":
                    $condition = $key . " = " . " :$key: ";
                    $bind[$key] = $value[1];
                    break;
                case "NEQ":
                    $condition = $key . " != " . " :$key: ";
                    $bind[$key] = $value[1];
                    break;
                case "GT":
                    $condition = $key . " > " . " :$key: ";
                    $bind[$key] = $value[1];
                    break;
                case "EGT":
                    $condition = $key . " >= " . " :$key: ";
                    $bind[$key] = $value[1];
                    break;
                case "LT":
                    $condition = $key . " < " . " :$key: ";
                    $bind[$key] = $value[1];
                    break;
                case "ELT":
                    $condition = $key . " <= " . " :$key: ";
                    $bind[$key] = $value[1];
                    break;
                case "LIKE":
                    $condition = $key . " like ( " . " :$key: )";
                    $bind[$key] = "%" . $value[1] . "%";
                    break;
                case "BETWEEN":
                    if (is_array($value[1])) {
                        $condition = $key . " BETWEEN  :between:  AND  :and: ";
                        $bind['between'] = $value[1][0];
                        $bind['and'] = $value[1][1];
                    }
                    break;
                case "IN":
                    if (is_array($value[1])) {
                        $condition = $key . " in ({" . $key . ":array}) ";
                        $bind[$key] = $value[1];
                    }
                    break;
                case "NOT IN":
                    if (is_array($value[1])) {
                        $condition = $key . " not in ({" . $key . ":array}) ";
                        $bind[$key] = $value[1];
                    }
                    break;
                case "FINDINSET":
                    $condition = "FIND_IN_SET(".":$key:".",".$key.")";
                    $bind[$key] = $value[1];
                    break;
                default:
                    break;
            }
        } else {
            $condition = $key . " = " . " :$key: ";
            $bind[$key] = $value;
        }

        return $condition;
    }

}
